<?php
namespace app\service;

use think\facade\Cache;
use think\facade\Db;
use think\facade\Config;
use think\facade\Log;
use think\facade\Request;
use think\Exception;

/**
 * 微信小程序登录服务类
 * Class WechatMiniProgramService
 * @package app\service
 */
class WechatMiniProgramService
{
    // 微信接口URL
    const WX_LOGIN_URL = 'https://api.weixin.qq.com/sns/jscode2session';

    // Token前缀
    const TOKEN_PREFIX = 'wechat_token:';

    // 用户信息缓存前缀
    const USER_CACHE_PREFIX = 'wechat_user:';

    // 会话缓存前缀
    const SESSION_CACHE_PREFIX = 'wechat_session:';

    /**
     * 处理微信小程序登录
     * @param string $code 微信登录code
     * @param array $userData 用户数据（可选）
     * @return array
     * @throws Exception
     */
    public function login($code, $userData = [])
    {
        try {
            // 1. 验证参数
            if (empty($code)) {
                throw new Exception('登录凭证code不能为空');
            }

            // 2. 获取微信配置
            $config = $this->getWechatConfig();

            // 3. 请求微信接口获取openid和session_key
            $wxData = $this->getWechatSession($code, $config['appid'], $config['secret']);

            if (empty($wxData['openid'])) {
                throw new Exception('获取OpenID失败');
            }

            $openid = $wxData['openid'];
            $sessionKey = $wxData['session_key'] ?? '';
            $unionid = $wxData['unionid'] ?? '';

            // 4. 解密用户数据（如果有加密数据）
            $decryptedUserInfo = [];
            if (!empty($userData['encryptedData']) && !empty($userData['iv']) && !empty($sessionKey)) {
                $decryptedUserInfo = $this->decryptUserInfo(
                    $userData['encryptedData'],
                    $userData['iv'],
                    $sessionKey,
                    $config['appid']
                );
            }

            // 5. 处理用户信息
            $userInfo = $this->processUserInfo($decryptedUserInfo, $userData);

            // 6. 查找或创建用户
            $user = $this->findOrCreateUser($openid, $unionid, $sessionKey, $userInfo);

            // 7. 生成Token
            $token = $this->generateToken($user['id'], $openid);

            // 8. 缓存用户信息和会话
            $this->cacheUserData($user, $token, $sessionKey);

            return [
                'success' => true,
                'token' => $token,
                'user_info' => $this->formatUserOutput($user),
                'expires_in' => $config['token_expire'] ?? 7200
            ];

        } catch (Exception $e) {
            throw new Exception('登录处理失败: ' . $e->getMessage());
        }
    }

    /**
     * 获取微信会话信息
     * @param string $code
     * @param string $appId
     * @param string $appSecret
     * @return array
     * @throws Exception
     */
    private function getWechatSession($code, $appId, $appSecret)
    {
        $params = [
            'appid' => $appId,
            'secret' => $appSecret,
            'js_code' => $code,
            'grant_type' => 'authorization_code'
        ];

        $url = self::WX_LOGIN_URL . '?' . http_build_query($params);
        $response = $this->httpRequest($url);

        if (!$response) {
            throw new Exception('微信接口请求失败');
        }

        $data = json_decode($response, true);
        Log::channel('schedule')->info($data);

        if (isset($data['errcode']) && $data['errcode'] != 0) {
            throw new Exception('微信接口错误: ' . ($data['errmsg'] ?? '未知错误'));
        }

        return $data;
    }

    /**
     * 解密用户信息
     * @param string $encryptedData
     * @param string $iv
     * @param string $sessionKey
     * @param string $appId
     * @return array
     * @throws Exception
     */
    private function decryptUserInfo($encryptedData, $iv, $sessionKey, $appId)
    {
        if (strlen($sessionKey) != 24) {
            throw new Exception('session_key长度无效');
        }

        if (strlen($iv) != 24) {
            throw new Exception('iv长度无效');
        }

        $aesKey = base64_decode($sessionKey);
        $aesIV = base64_decode($iv);
        $aesCipher = base64_decode($encryptedData);

        $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, OPENSSL_RAW_DATA, $aesIV);

        if ($result === false) {
            throw new Exception('数据解密失败');
        }

        $data = json_decode($result, true);

        if ($data === null) {
            throw new Exception('解密数据解析失败');
        }

        // 验证appid
        if (!isset($data['watermark']['appid']) || $data['watermark']['appid'] != $appId) {
            throw new Exception('appid验证失败');
        }

        return $data;
    }

    /**
     * 处理用户信息
     * @param array $decryptedData
     * @param array $rawUserData
     * @return array
     */
    private function processUserInfo($decryptedData, $rawUserData)
    {
        $userInfo = [];

        // 优先使用解密后的数据
        if (!empty($decryptedData)) {
            $userInfo = [
                'nickname' => $decryptedData['nickName'] ?? '',
                'avatar' => $decryptedData['avatarUrl'] ?? '',
                'gender' => $decryptedData['gender'] ?? 0,
                'city' => $decryptedData['city'] ?? '',
                'province' => $decryptedData['province'] ?? '',
                'country' => $decryptedData['country'] ?? '',
                'language' => $decryptedData['language'] ?? '',
                'raw_data' => json_encode($decryptedData, JSON_UNESCAPED_UNICODE)
            ];
        }
        // 其次使用原始数据
        elseif (!empty($rawUserData['rawData'])) {
            $rawData = json_decode($rawUserData['rawData'], true);
            if ($rawData) {
                $userInfo = [
                    'nickname' => $rawData['nickName'] ?? '',
                    'avatar' => $rawData['avatarUrl'] ?? '',
                    'gender' => $rawData['gender'] ?? 0,
                    'city' => $rawData['city'] ?? '',
                    'province' => $rawData['province'] ?? '',
                    'country' => $rawData['country'] ?? '',
                    'raw_data' => $rawUserData['rawData']
                ];
            }
        }

        return $userInfo;
    }

    /**
     * 查找或创建用户
     * @param string $openid
     * @param string $unionid
     * @param string $sessionKey
     * @param array $userInfo
     * @return array
     * @throws Exception
     */
    private function findOrCreateUser($openid, $unionid, $sessionKey, $userInfo)
    {
        $currentTime = date('Y-m-d H:i:s');

        // 查找现有用户
        $user = Db::name('box_users')
            ->where('openid', $openid)
            ->find();

        $userData = [
            'openid' => $openid,
            'unionid' => $unionid,
            'session_key' => $sessionKey,
            'last_login_time' => $currentTime
        ];

        // 合并用户信息
        if (!empty($userInfo)) {
            $userData = array_merge($userData, $userInfo);
        }

        if ($user) {
            // 更新用户信息
            Db::name('box_users')
                ->where('id', $user['id'])
                ->update($userData);

            $userData['id'] = $user['id'];
        } else {
            // 创建新用户
            $userData['create_time'] = $currentTime;
            $userId = Db::name('box_users')
                ->insertGetId($userData);

            $userData['id'] = $userId;
        }

        return $userData;
    }

    /**
     * 生成Token
     * @param int $userId
     * @param string $openid
     * @return string
     */
    private function generateToken($userId, $openid)
    {
        $config = $this->getWechatConfig();
        $expireTime = time() + ($config['token_expire'] ?? 7200);

        $payload = [
            'user_id' => $userId,
            'openid' => $openid,
            'iat' => time(),
            'exp' => $expireTime
        ];

        $salt = $config['token_salt'] ?? 'default-secret-salt';

        $header = base64_encode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
        $payload = base64_encode(json_encode($payload));
        $signature = hash_hmac('sha256', "{$header}.{$payload}", $salt, true);
        $signature = base64_encode($signature);

        return "{$header}.{$payload}.{$signature}";
    }

    /**
     * 缓存用户数据
     * @param array $user
     * @param string $token
     * @param string $sessionKey
     */
    private function cacheUserData($user, $token, $sessionKey)
    {
        $config = $this->getWechatConfig();
        $expire = $config['token_expire'] ?? 7200;

        // 缓存Token对应的用户ID
        Cache::set(self::TOKEN_PREFIX . $token, $user['id'], $expire);

        // 缓存用户信息
        Cache::set(self::USER_CACHE_PREFIX . $user['id'], $user, $expire);

        // 缓存session_key（如果需要）
        if (!empty($sessionKey)) {
            Cache::set(self::SESSION_CACHE_PREFIX . $user['id'], $sessionKey, 3600); // session_key有效期较短
        }
    }

    /**
     * 验证Token
     * @param string $token
     * @return array|false
     */
    public function verifyToken($token)
    {
        if (empty($token)) {
            return false;
        }

        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return false;
        }

        list($header, $payload, $signature) = $parts;

        $config = $this->getWechatConfig();
        $salt = $config['token_salt'] ?? 'default-secret-salt';

        // 验证签名
        $expectedSignature = hash_hmac('sha256', "{$header}.{$payload}", $salt, true);
        $expectedSignature = base64_encode($expectedSignature);

        if (!hash_equals($signature, $expectedSignature)) {
            return false;
        }

        // 解析payload
        $payloadData = json_decode(base64_decode($payload), true);
        if (!$payloadData) {
            return false;
        }

        // 检查过期时间
        if (isset($payloadData['exp']) && $payloadData['exp'] < time()) {
            return false;
        }

        // 验证Token是否在缓存中（可选，用于实现Token失效功能）
        $cachedUserId = Cache::get(self::TOKEN_PREFIX . $token);
        if (!$cachedUserId || $cachedUserId != $payloadData['user_id']) {
            return false;
        }

        return $payloadData;
    }

    /**
     * 获取用户信息
     * @param int $userId
     * @return array|false
     */
    public function getUserInfo($userId)
    {
        // 先从缓存获取
        $user = Cache::get(self::USER_CACHE_PREFIX . $userId);

        if (!$user) {
            // 缓存中没有，从数据库获取
            $user = Db::name('box_users')
                ->where('id', $userId)
                ->find();

            if ($user) {
                // 重新缓存
                $config = $this->getWechatConfig();
                Cache::set(self::USER_CACHE_PREFIX . $userId, $user, $config['token_expire'] ?? 7200);
            }
        }

        return $user ?: false;
    }

    /**
     * 刷新Token
     * @param string $oldToken
     * @return array|false
     */
    public function refreshToken($oldToken)
    {
        $payload = $this->verifyToken($oldToken);
        if (!$payload) {
            return false;
        }

        // 删除旧Token
        Cache::delete(self::TOKEN_PREFIX . $oldToken);

        // 生成新Token
        $newToken = $this->generateToken($payload['user_id'], $payload['openid']);

        // 获取用户信息
        $user = $this->getUserInfo($payload['user_id']);
        if (!$user) {
            return false;
        }

        // 缓存新Token
        $config = $this->getWechatConfig();
        Cache::set(self::TOKEN_PREFIX . $newToken, $payload['user_id'], $config['token_expire'] ?? 7200);

        return [
            'token' => $newToken,
            'user_info' => $this->formatUserOutput($user),
            'expires_in' => $config['token_expire'] ?? 7200
        ];
    }

    /**
     * 格式化用户输出
     * @param array $user
     * @return array
     */
    private function formatUserOutput($user)
    {
        return [
            'id' => $user['id'],
            'openid' => $user['openid'],
            'nickname' => $user['nickname'] ?? '',
            'avatar' => $user['avatar'] ?? '',
            'gender' => $user['gender'] ?? 0,
        ];
    }

    /**
     * 获取微信配置
     * @return array
     * @throws Exception
     */
    private function getWechatConfig()
    {
        $config = Config::get('wechat.mini_program', []);

        if (empty($config['appid']) || empty($config['secret'])) {
            throw new Exception('微信小程序配置不完整');
        }

        // 设置默认值
        $config['token_expire'] = $config['token_expire'] ?? 7200;
        $config['token_salt'] = $config['token_salt'] ?? 'default-secret-salt';

        return $config;
    }

    /**
     * HTTP请求
     * @param string $url
     * @param mixed $data
     * @param string $method
     * @return bool|string
     */
    private function httpRequest($url, $data = null, $method = 'GET')
    {
        $curl = curl_init();

        curl_setopt_array($curl, [
            CURLOPT_URL => $url,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_FOLLOWLOCATION => true
        ]);

        if (strtoupper($method) === 'POST') {
            curl_setopt($curl, CURLOPT_POST, true);
            if ($data) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
        }

        $response = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);

        curl_close($curl);

        return $httpCode === 200 ? $response : false;
    }

    /**
     * 注销登录
     * @param string $token
     * @return bool
     */
    public function logout($token)
    {
        if (empty($token)) {
            return false;
        }

        // 删除Token缓存
        Cache::delete(self::TOKEN_PREFIX . $token);

        return true;
    }

    /**
     * 获取用户ID
     * @param string $token
     * @return int|false
     */
    public function getUserIdByToken($token)
    {
        return Cache::get(self::TOKEN_PREFIX . $token);
    }
}